/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.icee.myth.deamon;

import com.icee.myth.deamon.channelContext.HeartbeatChannelContext;
import com.icee.myth.deamon.message.serverMessage.Message;
import com.icee.myth.deamon.messageQueue.ServerMessageQueue;
import com.icee.myth.deamon.utils.LogConsts;
import com.icee.myth.deamon.utils.MLogger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;

import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;

import static com.icee.myth.deamon.utils.Consts.RECONNECT_PERIOD;

/**
 * 抽象心跳客户端，这里的Client接口不是游戏客户端，是Server相互之间通信的Client。
 * @author liuxianke
 */
public abstract class AbstractHeartbeatClient implements Client {

    private String host; // Server的主机地址
    private int port; // Server的端口地址
    private ClientBootstrap bootstrap;
    protected final HeartbeatChannelContext channelContext; // channelContext connect to server
    private Timer connectTimer; //断线重连定时器

    public AbstractHeartbeatClient() {
        channelContext = new HeartbeatChannelContext();
    }

    public void init(String host, int port, LengthFieldBasedFrameDecoder lengthFieldBasedFrameDecoder,OneToOneEncoder customEncoder, ChannelUpstreamHandler targetHandler) {
        this.host = host;
        this.port = port;

        ChannelFactory factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
        this.bootstrap = new ClientBootstrap(factory);

        // Set up the default event pipeline.
        ChannelPipeline p = bootstrap.getPipeline();
        p.addLast("frameDecoder", lengthFieldBasedFrameDecoder);
        p.addLast("customEncoder", customEncoder);

        p.addLast("handler", targetHandler);

        this.bootstrap.setOption("tcpNoDelay", true);
        this.bootstrap.setOption("keepAlive", true);
    }

    @Override
    public void connectToServer() {
        // 连接服务
        // Start the connection attempt.
        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Connecting to server...");
        bootstrap.connect(new InetSocketAddress(host, port));
    }

    @Override
    public HeartbeatChannelContext getChannelContext() {
        return channelContext;
    }

    public void flush() {
        channelContext.flush();
    }

    protected void heartbeat() {
        // 向对方发送心跳消息
        channelContext.write(buildClientHeartBeat());

        // 心跳计数减一
        int beatNum = channelContext.heartbeat();
        // 心跳计数小于等于0表示发生故障
        if (beatNum <= 0) {
            // 当心跳计数为0时，向消息队列中产生一服务故障消息。
            if (beatNum == 0) {
                ServerMessageQueue.queue().offer(buildServerDownMessage());

                channelContext.close();
            }
            // TODO: 报警，当心跳计数小于等于0后产生连续的报警
            ServerDownWarning();
        }
    }

    protected void restoreHeartbeat() {
        channelContext.restoreHeartbeat();
    }

    protected void serverConnect(Channel channel) {
        channelContext.setChannel(channel);
        channelContext.restoreHeartbeat();
    }

    protected void serverClose() {
        channelContext.close();
        ServerCloseWarning();
    }

    protected void reconnect() {
        // 重新连接cluster server
        connectTimer = new Timer();
        connectTimer.schedule(
                new TimerTask() {

                    @Override
                    public void run() {
                        connectToServer();
                        connectTimer.cancel();
                    }
                }, RECONNECT_PERIOD);
    }

    protected abstract Object buildClientHeartBeat();

    protected abstract Message buildServerDownMessage();

    protected abstract void ServerDownWarning();

    protected abstract void ServerCloseWarning();
}
